package org.codehaus.activemq.message;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.IOException;
import javax.jms.BytesMessage;
import javax.jms.JMSException;
import javax.jms.MessageEOFException;
import javax.jms.MessageFormatException;
import javax.jms.MessageNotReadableException;
import javax.jms.MessageNotWriteableException;

public class ActiveMQBytesMessage extends ActiveMQMessage
  implements BytesMessage
{
  private DataOutputStream dataOut;
  private ByteArrayOutputStream bytesOut;
  private DataInputStream dataIn;

  public int getPacketType()
  {
    return 9;
  }

  public ActiveMQMessage shallowCopy()
    throws JMSException
  {
    ActiveMQBytesMessage other = new ActiveMQBytesMessage();
    initializeOther(other);
    try {
      other.setBodyAsBytes(getBodyAsBytes());
    }
    catch (IOException e) {
      JMSException jmsEx = new JMSException("setBodyAsBytes() failed");
      jmsEx.setLinkedException(e);
      throw jmsEx;
    }
    return other;
  }

  public ActiveMQMessage deepCopy()
    throws JMSException
  {
    ActiveMQBytesMessage other = new ActiveMQBytesMessage();
    initializeOther(other);
    try {
      if (getBodyAsBytes() != null) {
        byte[] data = new byte[getBodyAsBytes().length];
        System.arraycopy(getBodyAsBytes(), 0, data, 0, data.length);
        other.setBodyAsBytes(data);
      }
    }
    catch (IOException e) {
      JMSException jmsEx = new JMSException("setBodyAsBytes() failed");
      jmsEx.setLinkedException(e);
      throw jmsEx;
    }
    return other;
  }

  public void setBodyAsBytes(byte[] bodyAsBytes)
  {
    super.setBodyAsBytes(bodyAsBytes);
    this.dataOut = null;
    this.dataIn = null;
  }

  public byte[] getBodyAsBytes()
    throws IOException
  {
    if (this.dataOut != null) {
      this.dataOut.flush();
      super.setBodyAsBytes(this.bytesOut.toByteArray());
      this.dataOut.close();
      this.dataOut = null;
    }
    return super.getBodyAsBytes();
  }

  public void clearBody()
    throws JMSException
  {
    super.clearBody();
    this.dataOut = null;
    this.dataIn = null;
    this.bytesOut = null;
  }

  public long getBodyLength()
    throws JMSException
  {
    if (!this.readOnlyMessage) {
      throw new MessageNotReadableException("This message is in write-only mode");
    }
    long length = 0L;
    try {
      if (super.getBodyAsBytes() != null)
        length = super.getBodyAsBytes().length;
    }
    catch (IOException e)
    {
      JMSException jmsEx = new JMSException("getBodyAsBytes() failed");
      jmsEx.setLinkedException(e);
      throw jmsEx;
    }
    return length;
  }
  public boolean readBoolean() throws JMSException {
    initializeReading();
    JMSException jmsEx;
    try {
      return this.dataIn.readBoolean();
    }
    catch (EOFException eof) {
      jmsEx = new MessageEOFException(eof.getMessage());
      jmsEx.setLinkedException(eof);
      throw jmsEx;
    }
    catch (IOException ioe) {
      jmsEx = new JMSException("Format error occured" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
    }throw jmsEx;
  }
  public byte readByte() throws JMSException {
    initializeReading();
    JMSException jmsEx;
    try {
      return this.dataIn.readByte();
    }
    catch (EOFException eof) {
      jmsEx = new MessageEOFException(eof.getMessage());
      jmsEx.setLinkedException(eof);
      throw jmsEx;
    }
    catch (IOException ioe) {
      jmsEx = new JMSException("Format error occured" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
    }throw jmsEx;
  }
  public int readUnsignedByte() throws JMSException {
    initializeReading();
    JMSException jmsEx;
    try {
      return this.dataIn.readUnsignedByte();
    }
    catch (EOFException eof) {
      jmsEx = new MessageEOFException(eof.getMessage());
      jmsEx.setLinkedException(eof);
      throw jmsEx;
    }
    catch (IOException ioe) {
      jmsEx = new JMSException("Format error occured" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
    }throw jmsEx;
  }
  public short readShort() throws JMSException {
    initializeReading();
    JMSException jmsEx;
    try {
      return this.dataIn.readShort();
    }
    catch (EOFException eof) {
      jmsEx = new MessageEOFException(eof.getMessage());
      jmsEx.setLinkedException(eof);
      throw jmsEx;
    }
    catch (IOException ioe) {
      jmsEx = new JMSException("Format error occured" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
    }throw jmsEx;
  }
  public int readUnsignedShort() throws JMSException {
    initializeReading();
    JMSException jmsEx;
    try {
      return this.dataIn.readUnsignedShort();
    }
    catch (EOFException eof) {
      jmsEx = new MessageEOFException(eof.getMessage());
      jmsEx.setLinkedException(eof);
      throw jmsEx;
    }
    catch (IOException ioe) {
      jmsEx = new JMSException("Format error occured" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
    }throw jmsEx;
  }
  public char readChar() throws JMSException {
    initializeReading();
    JMSException jmsEx;
    try {
      return this.dataIn.readChar();
    }
    catch (EOFException eof) {
      jmsEx = new MessageEOFException(eof.getMessage());
      jmsEx.setLinkedException(eof);
      throw jmsEx;
    }
    catch (IOException ioe) {
      jmsEx = new JMSException("Format error occured" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
    }throw jmsEx;
  }
  public int readInt() throws JMSException {
    initializeReading();
    JMSException jmsEx;
    try {
      return this.dataIn.readInt();
    }
    catch (EOFException eof) {
      jmsEx = new MessageEOFException(eof.getMessage());
      jmsEx.setLinkedException(eof);
      throw jmsEx;
    }
    catch (IOException ioe) {
      jmsEx = new JMSException("Format error occured" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
    }throw jmsEx;
  }
  public long readLong() throws JMSException {
    initializeReading();
    JMSException jmsEx;
    try {
      return this.dataIn.readLong();
    }
    catch (EOFException eof) {
      jmsEx = new MessageEOFException(eof.getMessage());
      jmsEx.setLinkedException(eof);
      throw jmsEx;
    }
    catch (IOException ioe) {
      jmsEx = new JMSException("Format error occured" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
    }throw jmsEx;
  }
  public float readFloat() throws JMSException {
    initializeReading();
    JMSException jmsEx;
    try {
      return this.dataIn.readFloat();
    }
    catch (EOFException eof) {
      jmsEx = new MessageEOFException(eof.getMessage());
      jmsEx.setLinkedException(eof);
      throw jmsEx;
    }
    catch (IOException ioe) {
      jmsEx = new JMSException("Format error occured" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
    }throw jmsEx;
  }
  public double readDouble() throws JMSException {
    initializeReading();
    JMSException jmsEx;
    try {
      return this.dataIn.readDouble();
    }
    catch (EOFException eof) {
      jmsEx = new MessageEOFException(eof.getMessage());
      jmsEx.setLinkedException(eof);
      throw jmsEx;
    }
    catch (IOException ioe) {
      jmsEx = new JMSException("Format error occured" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
    }throw jmsEx;
  }
  public String readUTF() throws JMSException {
    initializeReading();
    JMSException jmsEx;
    try {
      return this.dataIn.readUTF();
    }
    catch (EOFException eof) {
      jmsEx = new MessageEOFException(eof.getMessage());
      jmsEx.setLinkedException(eof);
      throw jmsEx;
    }
    catch (IOException ioe) {
      jmsEx = new JMSException("Format error occured" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
    }throw jmsEx;
  }

  public int readBytes(byte[] value)
    throws JMSException
  {
    return readBytes(value, value.length);
  }
  public int readBytes(byte[] value, int length) throws JMSException {
    initializeReading();
    JMSException jmsEx;
    try {
      int n = 0;
      while (n < length) {
        int count = this.dataIn.read(value, n, length - n);
        if (count < 0) {
          break;
        }
        n += count;
      }
      if ((n == 0) && (length > 0)) {
        n = -1;
      }
      return n;
    }
    catch (EOFException eof) {
      jmsEx = new MessageEOFException(eof.getMessage());
      jmsEx.setLinkedException(eof);
      throw jmsEx;
    }
    catch (IOException ioe) {
      jmsEx = new JMSException("Format error occured" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
    }throw jmsEx;
  }

  public void writeBoolean(boolean value)
    throws JMSException
  {
    initializeWriting();
    try {
      this.dataOut.writeBoolean(value);
    }
    catch (IOException ioe) {
      JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
      throw jmsEx;
    }
  }

  public void writeByte(byte value)
    throws JMSException
  {
    initializeWriting();
    try {
      this.dataOut.writeByte(value);
    }
    catch (IOException ioe) {
      JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
      throw jmsEx;
    }
  }

  public void writeShort(short value)
    throws JMSException
  {
    initializeWriting();
    try {
      this.dataOut.writeShort(value);
    }
    catch (IOException ioe) {
      JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
      throw jmsEx;
    }
  }

  public void writeChar(char value)
    throws JMSException
  {
    initializeWriting();
    try {
      this.dataOut.writeChar(value);
    }
    catch (IOException ioe) {
      JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
      throw jmsEx;
    }
  }

  public void writeInt(int value)
    throws JMSException
  {
    initializeWriting();
    try {
      this.dataOut.writeInt(value);
    }
    catch (IOException ioe) {
      JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
      throw jmsEx;
    }
  }

  public void writeLong(long value)
    throws JMSException
  {
    initializeWriting();
    try {
      this.dataOut.writeLong(value);
    }
    catch (IOException ioe) {
      JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
      throw jmsEx;
    }
  }

  public void writeFloat(float value)
    throws JMSException
  {
    initializeWriting();
    try {
      this.dataOut.writeFloat(value);
    }
    catch (IOException ioe) {
      JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
      throw jmsEx;
    }
  }

  public void writeDouble(double value)
    throws JMSException
  {
    initializeWriting();
    try {
      this.dataOut.writeDouble(value);
    }
    catch (IOException ioe) {
      JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
      throw jmsEx;
    }
  }

  public void writeUTF(String value)
    throws JMSException
  {
    initializeWriting();
    try {
      this.dataOut.writeUTF(value);
    }
    catch (IOException ioe) {
      JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
      throw jmsEx;
    }
  }

  public void writeBytes(byte[] value)
    throws JMSException
  {
    initializeWriting();
    try {
      this.dataOut.write(value);
    }
    catch (IOException ioe) {
      JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
      throw jmsEx;
    }
  }

  public void writeBytes(byte[] value, int offset, int length)
    throws JMSException
  {
    initializeWriting();
    try {
      this.dataOut.write(value, offset, length);
    }
    catch (IOException ioe) {
      JMSException jmsEx = new JMSException("Could not write data:" + ioe.getMessage());
      jmsEx.setLinkedException(ioe);
      throw jmsEx;
    }
  }

  public void writeObject(Object value)
    throws JMSException
  {
    if (value == null) {
      throw new NullPointerException();
    }
    initializeWriting();
    if ((value instanceof Boolean)) {
      writeBoolean(((Boolean)value).booleanValue());
    }
    else if ((value instanceof Character)) {
      writeChar(((Character)value).charValue());
    }
    else if ((value instanceof Byte)) {
      writeByte(((Byte)value).byteValue());
    }
    else if ((value instanceof Short)) {
      writeShort(((Short)value).shortValue());
    }
    else if ((value instanceof Integer)) {
      writeInt(((Integer)value).intValue());
    }
    else if ((value instanceof Double)) {
      writeDouble(((Double)value).doubleValue());
    }
    else if ((value instanceof Long)) {
      writeLong(((Long)value).longValue());
    }
    else if ((value instanceof Float)) {
      writeFloat(((Float)value).floatValue());
    }
    else if ((value instanceof Double)) {
      writeDouble(((Double)value).doubleValue());
    }
    else if ((value instanceof String)) {
      writeUTF(value.toString());
    }
    else if ((value instanceof byte[])) {
      writeBytes((byte[])value);
    }
    else
      throw new MessageFormatException("Cannot write non-primitive type:" + value.getClass());
  }

  public void reset()
    throws JMSException
  {
    this.readOnlyMessage = true;
    if (this.dataOut != null) {
      try {
        this.dataOut.flush();
        super.setBodyAsBytes(this.bytesOut.toByteArray());
        this.dataOut.close();
      }
      catch (IOException ioe) {
        JMSException jmsEx = new JMSException("reset failed: " + ioe.getMessage());
        jmsEx.setLinkedException(ioe);
        throw jmsEx;
      }
    }
    this.bytesOut = null;
    this.dataIn = null;
    this.dataOut = null;
  }

  private void initializeWriting() throws MessageNotWriteableException {
    if (this.readOnlyMessage) {
      throw new MessageNotWriteableException("This message is in read-onlu mode");
    }
    if (this.dataOut == null) {
      this.bytesOut = new ByteArrayOutputStream();
      this.dataOut = new DataOutputStream(this.bytesOut);
    }
  }

  private void initializeReading() throws MessageNotReadableException {
    if (!this.readOnlyMessage)
      throw new MessageNotReadableException("This message is in write-only mode");
    try
    {
      byte[] data = super.getBodyAsBytes();
      if ((this.dataIn == null) && (data != null)) {
        ByteArrayInputStream bytesIn = new ByteArrayInputStream(data);
        this.dataIn = new DataInputStream(bytesIn);
      }
    }
    catch (IOException e) {
      MessageNotReadableException mnr = new MessageNotReadableException("getBodyAsBytes failed");
      mnr.setLinkedException(e);
      throw mnr;
    }
  }
}